#pragma once

#include "Property.h"

#include <MyFC/TextControl.h>
#include <MyFC/CheckBox.h>

#include <sstream>
#include <stdexcept>
#include <boost/shared_ptr.hpp>

namespace My {

/**
 * Base for accessors
 */
class Access {
public:
	virtual ~Access() { }
	virtual bool Accept(const EditableObject &) const = 0;
};

/**
 * Read-accessor
 */
template <typename T>
class ReadAccess : public virtual Access {
public:
	virtual bool GetValue(const EditableObject &, T &) const = 0;
};

/**
 * Write-accessor
 */
template <typename T>
class WriteAccess : public virtual Access {
public:
	virtual void SetValue(EditableObject &, const T &) const = 0;
};

template <typename T>
class ReadWriteAccess : 
	public virtual ReadAccess<T>, 
	public virtual WriteAccess<T> 
{
public:
	virtual bool ReadOnly() const = 0;
};

template <typename T>
class ReadWriteImpl : public ReadWriteAccess<T> {
public:
	typedef boost::shared_ptr< ReadAccess<T> >		ReadSPtr;
	typedef boost::shared_ptr< WriteAccess<T> >		WriteSPtr;
public:
	ReadWriteImpl(ReadSPtr pGet, WriteSPtr pSet = WriteSPtr())
		: m_pGet(pGet), m_pSet(pSet) { }
public:
	virtual bool Accept(const EditableObject & obj) const {
		return (!m_pGet || m_pGet->Accept(obj)) &&
			(!m_pSet || m_pSet->Accept(obj));
	}
	virtual bool GetValue(const EditableObject & obj, T & value) const { 
		if (!m_pGet) 
			return false;
		return m_pGet->GetValue(obj, value);
	}
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (!m_pSet) 
			return;
		m_pSet->SetValue(obj, value);
	}
	virtual bool ReadOnly() const
		{ return !m_pSet; }
private:
	ReadSPtr		m_pGet;
	WriteSPtr		m_pSet;
};

typedef boost::shared_ptr< ReadAccess<bool> > ReadBoolSPtr;

class DLL_API NotReadBool : public ReadAccess<bool> {
public:
	NotReadBool(ReadBoolSPtr const & pGet)
		: m_pGet(pGet) { }
public:
	virtual bool Accept(const EditableObject & obj) const
		{ return m_pGet->Accept(obj); }
	virtual bool GetValue(const EditableObject & obj, bool & value) const {
		if (!m_pGet->GetValue(obj, value))
			return false;
		value = !value;
		return true;
	}
public:
	ReadBoolSPtr m_pGet;
};

inline ReadBoolSPtr Not(ReadBoolSPtr const & pGet)
	{ return ReadBoolSPtr(new NotReadBool(pGet)); }

class DLL_API ReadTrue : public ReadAccess<bool> {
public:
	virtual bool Accept(const EditableObject & obj) const
		{ return true; }
	virtual bool GetValue(const EditableObject & obj, bool & value) const {
		value = true;
		return true;
	}
};

inline ReadBoolSPtr True()
	{ return ReadBoolSPtr(new ReadTrue); }

class DLL_API ReadFalse : public ReadAccess<bool> {
public:
	virtual bool Accept(const EditableObject & obj) const
		{ return true; }
	virtual bool GetValue(const EditableObject & obj, bool & value) const {
		value = false;
		return true;
	}
};

inline ReadBoolSPtr False()
	{ return ReadBoolSPtr(new ReadFalse); }

class DLL_API OrReadBool : public ReadAccess<bool> {
public:
	OrReadBool(ReadBoolSPtr const & pGet1, ReadBoolSPtr const & pGet2)
		: m_pGet1(pGet1), m_pGet2(pGet2) { }
public:
	virtual bool Accept(const EditableObject & obj) const
		{ return m_pGet1->Accept(obj) && m_pGet2->Accept(obj); }
	virtual bool GetValue(const EditableObject & obj, bool & value) const {
		if (!m_pGet1->GetValue(obj, value))
			return false;
		if (value)
			return true;
		if (!m_pGet2->GetValue(obj, value))
			return false;
		return true;
	}
public:
	ReadBoolSPtr m_pGet1, m_pGet2;
};

inline ReadBoolSPtr Or(ReadBoolSPtr const & pGet1, ReadBoolSPtr const & pGet2)
	{ return ReadBoolSPtr(new OrReadBool(pGet1, pGet2)); }

/**
 * A property that operates with a value of type T
 */
template <typename T>
class BasicProperty : public PropertyBase {
public:
	BasicProperty() : m_Action("Change Property") { }
	const String & Action() const
		{ return m_Action; }
	BasicProperty & Action(const String & s)
		{ m_Action = s; return * this; }
public:
	virtual bool ReadOnly(const EditableList &) const;
	virtual bool Visible(const EditableList &) const;
	bool GetValue(const EditableList &, T &);
	void SetValue(const EditableList &, const T &);
public: // Work with single object:
	virtual bool ReadOnly(const EditableObject &) const = 0;
	virtual bool Visible(const EditableObject &) const = 0;
	virtual bool GetValue(const EditableObject &, T &) const = 0;
	virtual void SetValue(EditableObject &, const T &) const = 0;
private:
	String	m_Action;
};

template <typename T>
class PropertyImpl : public virtual BasicProperty<T> { 
public:
	typedef boost::shared_ptr< ReadAccess<T> >			ReadSPtr;
	typedef boost::shared_ptr< WriteAccess<T> >			WriteSPtr;
	typedef boost::shared_ptr< ReadWriteAccess<T> >		ReadWriteSPtr;
public:
	PropertyImpl();
public:
// Set the ReadWrite access
	template <typename RW>
	PropertyImpl & ReadWrite(RW);
	template <typename S>
	PropertyImpl & ReadWrite(BasicProperty<S> &, T (S::*));
// Set the Read access
	template <typename R>
	PropertyImpl & Read(R);
	template <typename S>
	PropertyImpl & Read(BasicProperty<S> &, T (S::*));
// Set the Write access
	template <typename W>
	PropertyImpl & Write(W);
// Set the ReadOnly flag
	template <typename R>
	PropertyImpl & ReadOnly(R);
// Set the Visible flag
	template <typename R>
	PropertyImpl & Visible(R);
// Set the Hint
	PropertyImpl & Hint(const ResourceString &);
public:
	virtual String GetHint(const EditableList &) const;
	virtual bool Accept(const EditableObject &) const; 
public:
	virtual bool ReadOnly(const EditableObject &) const;
	virtual bool Visible(const EditableObject &) const;
	virtual bool GetValue(const EditableObject &, T &) const;
	virtual void SetValue(EditableObject &, const T &) const;
private:
	ResourceString	m_Hint;
	ReadSPtr		m_pGet;
	WriteSPtr		m_pSet;
	ReadBoolSPtr	m_pReadOnly;
	ReadBoolSPtr	m_pVisible;
};

template <typename T>
boost::shared_ptr< ReadAccess<T> > Read(boost::shared_ptr< ReadAccess<T> > pRead)
	{ return pRead; }

template <typename T>
boost::shared_ptr< WriteAccess<T> >	Write(boost::shared_ptr< WriteAccess<T> > pWrite)
	{ return pWrite; }

template <typename T>
boost::shared_ptr< ReadWriteAccess<T> >	ReadWrite(boost::shared_ptr< ReadWriteAccess<T> > pReadWrite)
	{ return pReadWrite; }

template <typename T>
class Property : public PropertyImpl<T> {
	typedef PropertyImpl<T> Base;
public:
	virtual Widget * CreateControl(const EditableList &);
	virtual void FreeControl(Widget *);
};

template <typename T>
class PersistentPropertyBase : public virtual BasicProperty<T> 
{
	typedef BasicProperty<T> Base;
public:
	virtual void Deserialize(xml::const_node const &, EditableObject &) const;
	virtual void Serialize(xml::node const &, const EditableObject &) const;
	virtual bool Serializable(const EditableList &) const
		{ return true; }
};

template <typename T>
class PersistentProperty : 
	public Property<T>,
	public PersistentPropertyBase<T>
{
public:
};

/**
 * Generic template for property controls that
 * can edit values of type T.
 * Default implementation uses a text widget and
 * iostream << and >> operators.
 */
template <typename T> 
class TypedControlBase : public TextControl {
public:
	TypedControlBase(BasicProperty<T> &, const EditableList &);
public:
	// TextControl implementation
	virtual bool GetText(String & s);
	virtual void SetText(const String & s);
	virtual void HandleUpdateUI();
private:
	BasicProperty<T> &	m_Property;
	EditableList		m_Objects;
};

template <typename T>
class TypedControl : public TypedControlBase<T> { 
public:
	TypedControl(BasicProperty<T> & p, const EditableList & list)
		: TypedControlBase<T>(p, list) { }
};

} // My

#include "Impl/TypedProperty.inl"
